#include "qrunningmanager.h"

#include "qbasedialogwidget.h"
#include "qbasedialog.h"
#include "qsettingmanager.h"

#include "../running/version.h"
#include "../shared/qprojectcore.h"
#include "../shared/host/qabstracthost.h"
#include "../shared/host/qprojecthost.h"
#include "../shared/qpagemanager.h"
#include "../device/qservermanager.h"
#include "../shared/qdrivermanager.h"
#include "../shared/driver/qabstractdriver.h"
#include "../shared/qusermanager.h"
#include "../shared/qkeyboardwidget.h"
#include "../shared/qdatamanager.h"
#include "../shared/sqlite/qdatabasemanager.h"
#include "../shared/qdata.h"

#include <QEvent>
#include <QDesktopWidget>
#include <QApplication>
#include <QUuid>
#include <QProcess>

QRunningManager::QRunningManager(const QString &status, QObject *parent):
    QObject(parent),
    m_project(NULL),
    m_main_window(new QMainWindow),
    m_dialog_base_widget(new QBaseDialogWidget(m_main_window)),
    m_last_widget(NULL),
    m_server_manager(NULL),
    m_setting_manager(new QSettingManager),
    m_status(status),
    m_scene(new QGraphicsScene),
    m_view(new QGraphicsView),
    m_timer(new QTimer(this)),
    m_last_focus_widget(NULL),
    m_keyboard_host(NULL),
    m_workthread(new QWorkThread),
    m_database_manager(NULL),
    m_data_save_timer(new QTimer)
{
    m_dialog_base_widget->setVisible(false);
    m_main_window->installEventFilter(this);

    m_setting_manager->load();

    m_main_window->setCentralWidget(m_view);
    m_view->setScene(m_scene);
    m_view->setFrameStyle(QFrame::NoFrame);

    connect(m_timer,SIGNAL(timeout()),this,SLOT(timer()));

    connect(m_workthread,SIGNAL(work()),this,SLOT(load_timer()));
    m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    connect(m_data_save_timer,SIGNAL(timeout()),this,SLOT(data_save_timer()));
}

QRunningManager::~QRunningManager()
{
    release();
    m_main_window->removeEventFilter(this);
    delete m_main_window;
    delete m_setting_manager;
}

void QRunningManager::load(const QString &path)
{
    m_project_path=path;
    m_workthread->start();
    //QTimer::singleShot(10,this,SLOT(load_timer()));
}

void QRunningManager::load_timer()
{
    release();
    m_project=new QProjectCore;

    m_device_info.m_soft_version=RUNNING_VERSION_STRING;
    m_device_info.m_device_type="Windows";
    m_device_info.m_uuid=m_setting_manager->get_value("Device.Uuid").toString();
    m_device_info.m_soft_status=m_status;
    if(m_device_info.m_uuid=="")
    {
        m_device_info.m_uuid=QUuid::createUuid().toString();
        m_setting_manager->set_value("Device.Uuid",m_device_info.m_uuid);
        m_setting_manager->save();
    }

    if(!m_project->open(m_project_path+"/config.sfb"))
    {
        m_device_info.m_project_name="";
        m_server_manager=new QServerManager(&m_device_info);
        connect(m_server_manager,SIGNAL(update_success()),this,SLOT(update_success()));
        load_signal(false);
        return;
    }
    m_device_info.m_project_core=m_project;
    m_device_info.m_project_name=m_project->get_project_host()->get_property_value("objectName").toString();

    m_server_manager=new QServerManager(&m_device_info);
    connect(m_server_manager,SIGNAL(update_success()),this,SLOT(update_success()));
    connect(m_project->get_project_host(),SIGNAL(show_widget(QWidget*)),this,SLOT(show_widget(QWidget*)));
    connect(m_project->get_project_host(),SIGNAL(show_dialog(QAbstractHost*)),this,SLOT(show_dialog(QAbstractHost*)));

    m_database_manager=new QDataBaseManager;
    m_database_manager->load(m_project_path);

    m_database_manager->set_project_property("device_uuid",m_device_info.m_uuid);
    m_database_manager->set_project_property("device_type",m_device_info.m_device_type);
    m_database_manager->set_project_property("soft_version",m_device_info.m_soft_version);
    m_database_manager->set_project_property("soft_state",m_device_info.m_soft_status);
    m_database_manager->set_project_property("project_uuid",m_project->get_project_host()->get_uuid());
    m_database_manager->set_project_property("project_name",m_device_info.m_project_name);
    m_project->set_database_manager(m_database_manager);

    QMapIterator<QString,QString> it(m_database_manager->get_hold_data());

    QDataManager *manager=m_project->get_data_manager();
    while(it.hasNext())
    {
        it.next();
        manager->set_value(it.key(),it.value());
    }

    m_data_save_timer->start(100);

    connect(manager,SIGNAL(data_value_changed(QString)),this,SLOT(data_changed(QString)));

    init_script_engine();

    connect_exec();

    emit load_signal(true);
}

void QRunningManager::release()
{
    if(m_project!=NULL)
    {
        m_project->log("System","Software closed");
    }
    m_timer->stop();
    m_data_save_timer->stop();
    m_last_focus_widget=NULL;
    if(m_database_manager!=NULL)
    {
        delete m_database_manager;
        m_database_manager=NULL;
    }
    m_keyboard_host=NULL;
    QMapIterator<QWidget*,QGraphicsProxyWidget*> it(m_widget_to_proxy);
    while(it.hasNext())
    {
        it.next();
        it.value()->setWidget(NULL);
    }
    m_widget_to_proxy.clear();
    m_scene->clear();

    if(m_project!=NULL)
    {
        delete m_project;
        m_project=NULL;
    }
    m_last_widget=NULL;

    if(m_server_manager!=NULL)
    {
        delete m_server_manager;
        m_server_manager=NULL;
    }
}

void QRunningManager::show_widget(QWidget *widget)
{
    if(widget==NULL)
    {
        return;
    }
    QGraphicsProxyWidget *proxy=m_widget_to_proxy.value(widget);
    if(proxy==NULL)
    {
        proxy=m_scene->addWidget(widget);
        m_widget_to_proxy.insert(widget,proxy);
    }

    if(m_last_widget!=NULL)
    {
        m_widget_to_proxy.value(m_last_widget)->setVisible(false);
    }
    m_last_widget=widget;
    proxy->setVisible(true);
}

void QRunningManager::show_dialog(QAbstractHost *host)
{
    m_dialog_base_widget->setVisible(true);
    m_dialog_base_widget->raise();

    QWidget* wid=(QWidget*)host->get_object();

    QBaseDialog dlg(m_main_window);

    dlg.set_widget(wid);

    dlg.exec();

    wid->setParent(NULL);
}

bool QRunningManager::eventFilter(QObject *o, QEvent *e)
{
    if(o==m_main_window && e->type()==QEvent::Close)
    {
        stop();
        return true;
    }
    return false;
}

void QRunningManager::start()
{
    QProjectHost* host=(QProjectHost*)m_project->get_project_host();
    QString start_page=host->get_property_value("start_page").toString();

    QPageManager *manager=m_project->get_page_manager();
    QList<QAbstractHost*> list=manager->get_pages();
    foreach(QAbstractHost* page,list)
    {
        ((QWidget*)page->get_object())->setVisible(false);
    }

    QSize sz=host->get_property_value("running_size").toSize();
    m_main_window->setFixedSize(sz);

    m_main_window->setWindowTitle(host->get_property_value("objectName").toString());

    QDesktopWidget *desk=qApp->desktop();

    m_main_window->move((desk->width()-sz.width())/2,(desk->height()-sz.height())/2);

    host->show_form_by_uuid(start_page);


    m_main_window->setVisible(true);

    foreach(QAbstractDriver *driver,m_project->get_driver_manager()->get_drivers())
    {
        driver->start();
    }

    m_project->log("System","Software startup");

    m_timer->start(100);

    emit start_signal();
}

void QRunningManager::stop()
{
    emit stop_signal();
    release();
    m_main_window->setVisible(false);
}

void QRunningManager::update_success()
{
    QProcess::startDetached(qApp->applicationDirPath()+"/softUpdate.exe");
    qApp->exit(0);
}

void QRunningManager::init_script_engine()
{
    m_global_value=m_script_engine.globalObject();
    QProjectHost* host=(QProjectHost*)m_project->get_project_host();

    QScriptValue g=m_script_engine.newQObject(host);
    m_global_value.setProperty("global",g);

    QList<QAbstractHost*> list=m_project->get_page_manager()->get_pages();
    foreach(QAbstractHost* page,list)
    {
        QScriptValue value=get_host_value(page);
        m_global_value.setProperty(page->get_property_value("objectName").toString(),value);
    }

    QList<QAbstractDriver*> l=m_project->get_driver_manager()->get_drivers();
    foreach(QAbstractDriver* driver,l)
    {
        QScriptValue value=get_host_value((QAbstractHost*)driver);
        m_global_value.setProperty(driver->get_property_value("objectName").toString(),value);
    }

    m_script_engine.setGlobalObject(m_global_value);
}

QScriptValue QRunningManager::get_host_value(QAbstractHost *host)
{
    QScriptValue value=m_script_engine.newQObject(host);

    foreach(QAbstractHost* h,host->get_children())
    {
        value.setProperty(h->get_property_value("objectName").toString(),get_host_value(h));
    }
    return value;
}

void QRunningManager::exec(const QString &code,
                           const QMap<QString, QVariant> &param,
                           QMap<QString, QVariant> &ret)
{
    m_script_engine.setGlobalObject(m_global_value);
    QScriptValue global=m_script_engine.globalObject();

    QMapIterator<QString, QVariant> it(param);
    while(it.hasNext())
    {
        it.next();
        global.setProperty("param_"+it.key(),get_script_value(it.value()));
    }

    QMapIterator<QString, QVariant> itt(ret);
    while(itt.hasNext())
    {
        itt.next();
        global.setProperty("return_"+itt.key(),get_script_value(itt.value()));
    }

    m_script_engine.setGlobalObject(global);
    m_script_engine.evaluate(code);

    if(m_script_engine.hasUncaughtException())
    {
        qDebug(m_script_engine.uncaughtException().toString().toLocal8Bit());
    }
    else
    {
        global=m_script_engine.globalObject();
        QMap<QString, QVariant> r;
        QMapIterator<QString, QVariant> t(ret);
        while(t.hasNext())
        {
            t.next();
            r.insert(t.key(),global.property("return_"+t.key()).toVariant());
        }
        ret=r;
    }
}

void QRunningManager::do_exec(const QString &code, const QMap<QString, QVariant> &param, QMap<QString, QVariant> &ret)
{
    int level=param.value("operatior_level").toInt();

    QString str=m_project->get_project_host()->get_property_value("start_user").toString();

    tagUserInfo *user=m_project->get_user_manager()->get_user(str);

    if(user!=NULL && user->m_level<level && level>0)
    {
        qDebug(tr("Permission denied").toLocal8Bit());
        return;
    }
    QMap<QString,QVariant> p=param;
    p.remove("operatior_level");

    exec(code,p,ret);
}

QScriptValue QRunningManager::get_script_value(const QVariant &value)
{
    QVariant::Type type=value.type();

    if(type==QVariant::Int)
    {
        return QScriptValue(value.toInt());
    }
    else if(type==QVariant::Double)
    {
        return QScriptValue(value.toReal());
    }
    else if(type==QVariant::Bool)
    {
        return QScriptValue(value.toBool());
    }
    else
    {
        return QScriptValue(value.toString());
    }
}

void QRunningManager::connect_exec()
{
    QList<QAbstractHost*> list;

    list.append(m_project->get_project_host());

    list+=m_project->get_page_manager()->get_pages();

    foreach(QAbstractDriver* driver,m_project->get_driver_manager()->get_drivers())
    {
        list.append(driver);
    }

    while(list.size()>0)
    {
        QAbstractHost* host=list.takeFirst();

        connect(host,SIGNAL(exec(QString,QMap<QString,QVariant>,QMap<QString,QVariant>&)),
                this,SLOT(do_exec(QString,QMap<QString,QVariant>,QMap<QString,QVariant>&)));
        connect(host,SIGNAL(changed_data(QString,QVariant)),this,SLOT(changed_data(QString,QVariant)));
        connect(host,SIGNAL(changed_property(QString,QVariant)),this,SLOT(changed_property(QString,QVariant)));
        host->update_memery_data();
        list+=host->get_children();
    }
}

void QRunningManager::timer()
{
    QGraphicsProxyWidget *wid=qgraphicsitem_cast<QGraphicsProxyWidget*>(m_scene->focusItem());

    if(wid!=NULL && wid->isVisible())
    {
        QObjectList list;
        list.append(wid->widget());
        QWidget *wid;
        while(list.size()>0)
        {
            wid=qobject_cast<QWidget*>(list.takeFirst());
            if(wid!=NULL)
            {
                if(wid->hasFocus())
                {
                    if(wid!=m_last_focus_widget)
                    {
                        focus_changed(wid);
                    }
                    return;
                }
                list+=wid->children();
            }
        }
    }
    else
    {
        if(m_last_focus_widget!=NULL)
        {
            focus_changed(NULL);
        }
    }
}

void QRunningManager::focus_changed(QWidget *now)
{
    if(m_last_focus_widget!=now)
    {
        m_last_focus_widget=now;
        if(now!=NULL)
        {
            QString key=now->property("keyboard").toString();

            if(key!="")
            {
                show_keyboard(m_project->get_page_manager()->get_page(key));
            }
            else
            {
                show_keyboard(NULL);
            }
        }
        else
        {
            show_keyboard(NULL);
        }
    }
}

void QRunningManager::show_keyboard(QAbstractHost *host)
{
    QKeyBoardWidget *wid;
    if(m_keyboard_host!=NULL)
    {
        QGraphicsProxyWidget *proxy=m_widget_to_proxy.value((QWidget*)m_keyboard_host->get_object());
        proxy->setVisible(false);
        wid=(QKeyBoardWidget*)m_keyboard_host->get_object();
        wid->set_focus_widget(NULL);
    }
    m_keyboard_host=host;
    if(m_keyboard_host!=NULL)
    {
        QGraphicsProxyWidget *proxy=m_widget_to_proxy.value((QWidget*)m_keyboard_host->get_object());
        if(proxy==NULL)
        {
            proxy=m_scene->addWidget((QWidget*)m_keyboard_host->get_object());
            proxy->setFocusPolicy(Qt::NoFocus);
            proxy->setZValue(2);
            m_widget_to_proxy.insert((QWidget*)host->get_object(),proxy);
        }
        proxy->setVisible(true);
        QPoint pt=host->get_property_value("position").toPoint();
        proxy->setPos(pt);
        wid=(QKeyBoardWidget*)m_keyboard_host->get_object();
        wid->set_focus_widget(m_last_focus_widget);
    }
}

void QRunningManager::changed_data(const QString &uuid, const QVariant &value)
{
    m_project->get_data_manager()->set_value(uuid,value);
}

void QRunningManager::changed_property(const QString &name, const QVariant &value)
{
    QAbstractHost* host=(QAbstractHost*)sender();
    host->set_property_value(name,value);
}

void QRunningManager::data_changed(const QString &uuid)
{
    tagDataInfo *info=m_project->get_data_manager()->get_data(uuid);

    if(info!=NULL && info->m_hold)
    {
        m_database_manager->set_hold_data(uuid,info->m_value);
    }
}

void QRunningManager::data_save_timer()
{
    QDateTime dt=QDateTime::currentDateTime();

    QList<QData*> datas=m_project->get_data_manager()->get_all_datas();

    QList<tagDataInfo*> list;

    foreach(QData* d,datas)
    {
        list+=d->get_datas();
    }

    QString str;
    str.sprintf("%04d-%02d-%02d %02d:%02d:%02d",dt.date().year(),
                dt.date().month(),dt.date().day(),
                dt.time().hour(),dt.time().minute(),dt.time().second());

    foreach(tagDataInfo* info,list)
    {
        if(info->m_save_time>0)
        {
            QDateTime d=m_last_save_time.value(info->m_uuid);
            if(!d.isValid() || d.msecsTo(dt)>=info->m_save_time*1000)
            {
                m_database_manager->save_data(info,str);
                m_last_save_time.insert(info->m_uuid,dt);
            }
        }
    }
}
